home *** CD-ROM | disk | FTP | other *** search
- .XLIST
- ;***********************************************************************;
- ; ;
- ;PASCAL.MAC ;
- ;(C)(P)Copyright 1983 by Eric S. Raymond ;
- ; These macros are useful for generating ASM-86 routines that ;
- ; do stack-based argument handling, and can thus be treated like ;
- ; Pascal-86 or BASIC routines once declared in an interface or ;
- ; EXTERNed to. Note that they must be PUBLICed and live in a ;
- ; SEGMENT PUBLIC PARA 'CODE' to be accessible to the linker. ;
- ; These macros should work correctly with any compiler that ;
- ; a) only requires BP to be saved during subroutine calls, and ;
- ; b) pushes arguments left to right (last given is last pushed). ;
- ; ;
- ;I) General service routines ;
- ; To generate a routine with a given profile of value and VAR ;
- ; args and a given service routine, write the following; ;
- ; ;
- ;[name] PROC ;
- ; PROLOG <arg1, arg2,...argn>, tr ;
- ; [service code] ;
- ; EPILOG reg ;
- ;[name] ENDP ;
- ; ;
- ; If A stands for an accumulator name, argk may have the form ;
- ; A -- argument k is a value argument to be moved to A ;
- ; #A -- argument k is a CONST argument (input only) ;
- ; *A -- argument k is a VAR argument (output only) ;
- ; @A -- argument k is a VAR argument (input and output) ;
- ; For these four cases, PROLOG fetches argument k to A and EPILOG ;
- ; automatically stores A to it (if it's VAR). ;
- ; If argk has none of these forms, it is simply equated to k ;
- ; in the assembler's symbol tables, and can be used with GETVAL, ;
- ; GETVAR, and SETVAR to give the effect of named arguments. ;
- ; If a register reg is specified, that register will be loaded ;
- ; into AL or AX (depending on length) just before exit and Pascal ;
- ; will see it as a return value. If reg is CY, AL will be loaded ;
- ; with 1 or 0 as the carry flag is on or off just before exit. ;
- ; Since the stack loads don't change the state of the flags, AL ;
- ; may pass out a VAR argument as usual before this. ;
- ; By default SI is used as a scratch register for locations of ;
- ; VAR arguments. This will lose if a VAR or CONST argument occurs ;
- ; after SI in the argument list. The optional second argument tr ;
- ; of PROLOG may be used to specify another scratch register; it ;
- ; may be SI, DI or BX and is passed to GETVAR and SETVAR. ;
- ; ;
- ;II) GETVAL, GETVAR and SETVAR ;
- ; The macros GETVAL reg, k and GETVAR reg, k will load a register ;
- ; from the kth value or location argument respectively. The macro ;
- ; SETVAR k, reg will store REG to the kth by-location argument. ;
- ; GETVAR and SETVAR take an optional third argument which sets ;
- ; the location scratch register as described above. ;
- ; ;
- ;III) WITH1, WITH2 ;
- ; The macros WITH1 and WITH2 allow any instruction that will take ;
- ; the appropriate addressing mode to be applied to two operands, ;
- ; one of which is a stacked argument represented by its name. For ;
- ; example: ;
- ; WITH2 CMP, AX, FOO ;
- ; expands to CMP AX, [BP+X*2] where X is the stack offset of the ;
- ; argument named by FOO. WITH1 does this to its second argument, ;
- ; so WITH1 CMP, FOO, AX would be equivalent to the above. ;
- ; ;
- ;IV) SYS function calls ;
- ; A macro has been included for generating interfaces to the ;
- ; PC's BIOS and DOS interrupt servers. With this macro, ;
- ; ;
- ; SYS name, int, func, <arg1, arg2,... argn>, reg, tr ;
- ; ;
- ; generates source for a Pascal-accessible routine that executes ;
- ; PROLOG, does an INT (int) with (func) in AH, and then does ;
- ; EPILOG. Arguments in the bracketed list are loaded and returned ;
- ; as in PROLOG/EPILOG. Optional args reg and tr are as above. ;
- ; SYS will PUBLIC the generated function, though this won't ;
- ; be obvious in an .XALL listing since PUBLIC generates no code. ;
- ; ;
- ;***********************************************************************;
-
- .XCREF
-
- INTFUN MACRO INTN, FUNCN ;;Call a BIOS function
- MOV AH, FUNCN ;; with given function number
- INT INTN ;; and given interrupt
- ENDM
-
- ACOUNT MACRO ARG ;;Counts arguments
- IRP REG, <AL,AH,BL,BH,CL,CH,DL,DH,AX,BX,CX,DX,SI,DI>
- IFIDN <ARG>, <REG> ;;If it's a value argument
- INCT = INCT + 1 ;; bump the input argument count
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- IFIDN <ARG>, <#®> ;;If it's a CONST argument
- INCT = INCT + 1 ;; bump the input argument count
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- IFIDN <ARG>, <*®> ;;If it's an output-only VAR arg
- OUTCT = OUTCT + 1 ;; bump the output argument count
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- IFIDN <ARG>, <@®> ;;If it's a VAR argument
- IOCT = IOCT + 1 ;; bump the input/output arg count
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- LACT = LACT + 1 ;;It's a named argument
- ENDM ;;End of IRP
- ENDM ;;End of ACOUNT
-
- GETVAL MACRO Z, N ;;Load Nth value arg to Z
- MOV Z, [BP+2*(ARITY-N)+6] ;;Move parameter to register
- ENDM ;;End of GETVAL
-
- GETVAR MACRO Z, N, T ;;Load Z to Nth VAR argument
- IFB <T> ;;If no transport reg specified
- MOV SI, [BP+2*(ARITY-N)+6] ;; fetch parameter address
- MOV Z, [SI] ;; and load to where it points
- ELSE ;;Else transport register given
- MOV T, [BP+2*(ARITY-N)+6] ;; fetch parameter address
- MOV Z, [T] ;; and load to where it points
- ENDM ;;End of GETVAR
-
- INARG MACRO FML, K, TR ;;Generate stack fetch for arg K
- ;;First check the null case
- IFB <FML> ;;If blank,
- %OUT K : skipped ;; note: stack slot is skipped
- EXITM ;; and exit INARG
- ENDIF ;;End of blank check
- ;;Now look for a matching register argument
- ISR = 0 ;;No register match yet found
- IRP REG, <AL,AH,BL,BH,CL,CH,DL,DH,AX,BX,CX,DX,SI,DI,SP>
- ;;Handle value arguments
- IFIDN <FML>, <REG> ;;If it's a value argument
- ISR = 1 ;; note that it matched
- GETVAL REG, K ;; fetch it
- %OUT K : VAL REG ;; and show it in the listing
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- ;;Handle CONST arguments
- IFIDN <FML>, <#®> ;;If it's CONST
- ISR = 1 ;; note that it matched
- GETVAR REG, K, TR ;; fetch Kth location arg
- %OUT K : CONST REG ;; and show it in the listing
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- ;;Handle two-way VAR arguments
- IFIDN <FML>, <@®> ;;If it's VAR
- ISR = 1 ;; note that it matched
- GETVAR REG, K, TR ;; fetch Kth location arg
- %OUT K : VAR REG ;; and show it in the listing
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- ;;Handle output VAR arguments
- IFIDN <FML>, <*®> ;;If it's an output-only arg
- ISR = 1 ;; note that it matched
- %OUT K : VAR REG (out only) ;; show it in the listing
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- ENDM ;;End of IRP
- ;;If no matching register arg, equate name to arg number
- IFE ISR ;;No register argument match
- FML = K ;;Equate name to arg value
- %OUT K : FML = K ;;Report the action
- ENDIF ;;End of symbol check
- ENDM ;;End of INARG
-
- PROLOG MACRO ARGS, TR ;;Process input arguments
- .XCREF ;;Don't need generated code to be CREFed
- ARITY = 0 ;;Start with 0 total arguments
- INCT = 0 ;;Start with 0 input args
- OUTCT = 0 ;;Start with 0 output args
- IOCT = 0 ;;Start with 0 input/output args
- LACT = 0 ;;Start with 0 named arguments
- IRP X, <ARGS> ;;Count the flavors of arguments
- ARITY = ARITY + 1 ;; ARITY counts all four kinds
- ACOUNT X ;; Now count the individual kinds
- ENDM ;;End of argument count loop
- PUSH BP ;;Save that frame pointer
- IF INCT+IOCT+LACT ;;If there are input args
- MOV BP, SP ;; set up for stack access
- C = 0 ;; initialize argument count
- IRP X, <ARGS> ;; and loop through the argument list
- C = C + 1 ;; using C to count from 1 to ARITY
- INARG X, %C, TR ;; generating stack fetches as we go
- ENDM ;;End of argument-list processing loop
- ENDIF ;;End of 'if there are input args'
- EPILOG MACRO REG ;;Generate EPILOG macro
- ENDF <ARGS>, REG, TR ;;Have it call ENDF with ARGLIST
- ENDM ;;End of generated macro
- .CREF ;;Restore CREF
- ENDM ;;End of PROLOG
-
- SETVAR MACRO N, Z, T ;;Store Z to Nth VAR argument
- IFB <T> ;;If no transport reg specified
- MOV SI, [BP+2*(ARITY-N)+6] ;; fetch parameter address
- MOV [SI], Z ;; and load to where it points
- ELSE ;;Else transport register given
- MOV T, [BP+2*(ARITY-N)+6] ;; fetch parameter address
- MOV [T], Z ;; and load to where it points
- ENDM ;;End of SETVAR
-
- OUTARG MACRO K, FML, TR ;;Gen stack load for Kth VAR arg
- IFNB <FML> ;;If blank, do nothing
- IRP REG, <AL,AH,AX,BL,BH,BX,CL,CH,CX,DL,DH,DX>
- IFIDN <FML>, <*®> ;;If it's an output-only arg
- SETVAR K, REG, TR ;; store to its location
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- IFIDN <FML>, <@®> ;;If it's a VAR argument
- SETVAR K, REG, TR ;; store to its location
- EXITM ;; then exit the IRP
- ENDIF ;; else continue list check
- ENDM ;;End of IRP
- ENDIF ;;Skip if <ARG> is blank
- ENDM ;;End of OUTARG
-
- MOVACC MACRO REG ;;Gen code to move REG to AL/AX
- %OUT REG value returned ;;Show it in the listing
- IFDIF <RETREG>, <AX> ;;Skip the rigamarole if it's AX
- IFDIF <RETREG>, <AL> ;; likewise if it's AL
- IRP X, <AH,CH,DH,BH,CL,DL,BL> ;;Try 8-bit registers
- IFIDN <REG>, <X> ;;If REG is one
- MOV AL, REG ;; then move it to AL
- EXITM ;; then exit the IRP
- ENDIF ;; else try the next one
- ENDM ;;End of 8-bit register IRP
- IRP X, <CX,DX,BX,SI,DI,BP,SP> ;;Try 16-bit registers
- IFIDN <REG>, <X> ;;If REG is one
- MOV AX, REG ;; then move it to AX
- EXITM ;; then exit the IRP
- ENDIF ;; else try the next one
- ENDM ;;End of 16-bit register IRP
- IFIDN <REG>, <CY> ;;Should carry flag be returned?
- XOR AL, AL ;; If so, zero AL
- RCR AL ;; and rotate in the carry bit
- ENDIF ;;End of carry bit processing
- ENDIF ;;Skip here if RETREG was AL
- ENDIF ;;Skip here if RETREG was AX
- ENDM ;;End of MOVACC
-
- ENDF MACRO ARGS,RETREG,TR ;;Gen stack loads and RET for routine
- .XCREF ;;Don't need cross-referencing here
- IF OUTCT + IOCT ;;If there are output arguments
- MOV BP, SP ;; set up for stack access
- C = 0 ;; initialize arg ctr
- IRP X, <ARGS> ;; and loop through it,
- C = C + 1 ;; using C to count from 1 to ARITY
- OUTARG %C, X, TR ;; generating stack loads as we go
- ENDM ;;End of argument list processing loop
- ENDIF ;;Now handle the return
- IFNB <RETREG> ;;If a return reg has been specified
- MOVACC RETREG ;; generate code to move it to AL or AX
- ENDIF ;;skip here if RETREG was blank
- POP BP ;;Restore frame ptr
- RET 2*ARITY ;;Clean args off stack
- %OUT ;;Make spacing blank line in listing
- .CREF ;;Restore cross-ref'ing for next routine
- ENDM ;;End of ENDF
-
- WITH1 MACRO OP, K, ARG ;;Apply OP to
- OP [BP+2*(ARITY-K)+6], ARG ;;Kth stack entry & ARG
- ENDM ;;End of WITH1
-
- WITH2 MACRO OP, ARG, K ;;Apply OP to
- OP ARG, [BP+2*(ARITY-K)+6] ;;ARG & Kth stack entry
- ENDM ;;End of WITH2
-
- SYS MACRO NAME, INTN, FUNCN, ARGS, REG ;;Gen SYS call interface
- %OUT NAME ;;Let user know we're here
- PUBLIC NAME ;;Make sure proc is accessible to Pascal
- NAME PROC FAR ;;Start of generated procedure
- PROLOG <ARGS> ;;Count & fetch input arguments
- INTFUN INTN, FUNCN ;;Call the interrupt function
- EPILOG REG ;;Call the macro generated by PROLOG
- NAME ENDP ;;End of generated procedure
- ENDM ;;End of SYS
-
- .LALL
- .CREF
- .LIST